﻿using LiteBlog.Common.Entities;
using LiteBlog.Common.Enums;
using LiteBlog.Repositories.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using LiteBlog.XmlLayer.Mocks.Constants;
using LiteBlog.Repositories.Helpers;

namespace LiteBlog.Repositories.Tests
{
    [TestClass]
    public class DraftDataTests : RepositoryTests<DraftRepository>
    {
       
        protected override void SetupLoad()
        {
           
        }

        protected override string GetFileName()
        {
            return "Draft.xml";
        }

        [TestMethod]
        public void ConstructorLoadsDrafts()
        {
            _mock.Verify(x => x.Load(It.Is<string>(p => VerifyPath(p))));
        }

        [TestMethod]
        public void GetWorks()
        {
            var posts = _repository.Get();
            Assert.IsTrue(posts.Any());
            var post = posts[0];
            Assert.AreEqual(DraftDataTestConstants.DraftId, post.FileID);
            Assert.AreEqual(DraftDataTestConstants.Title, post.Title);
            Assert.AreEqual(DraftDataTestConstants.FormattedTime, post.FormattedTime);
        }

        [TestMethod]
        public void GetScheduledWorks()
        {
            var posts = _repository.GetScheduled();
            Assert.IsTrue(posts.Any());
            var post = posts[0];
            Assert.AreEqual(DraftDataTestConstants.ScheduledDraftId, post.FileID);
            Assert.AreEqual(DraftDataTestConstants.ScheduledTitle, post.Title);
            Assert.AreEqual(PostType.Scheduled, post.Type);
        }

        [TestMethod]
        public void CreateWorks()
        {
            _repository.Create(DraftDataTestConstants.InsertId,
                          string.Empty,
                          DraftDataTestConstants.InsertTitle,
                          new List<Category>()
                          {
                              new Category
                              {
                                  CatID = DraftDataTestConstants.InsertCategory,
                                  Name = "Insert Category Name"
                              }
                          });
            _mock.Verify(x => x.Save(It.Is<XElement>(elem => VerifyCreate(elem)),
                                     It.Is<string>(p => VerifyPath(p))));
        }

        [TestMethod]
        public void UpdateWorks()
        {
            var postInfo = new PostInfo
            {
                FileID = DraftDataTestConstants.DraftId,
                Title = DraftDataTestConstants.UpdateTitle,
                Categories = new List<Category>
                {
                    new Category
                    {
                        CatID = DraftDataTestConstants.UpdateCategory1
                    },
                    new Category
                    {
                        CatID = DraftDataTestConstants.UpdateCategory2
                    }
                }
            };
            _repository.Update(postInfo);
            _mock.Verify(x => x.Save(It.Is<XElement>(elem => VerifyUpdate(elem)),
                                     It.Is<string>(p => VerifyPath(p))));
        }

        [TestMethod]
        public void DeleteWorks()
        {
            _repository.Delete(DraftDataTestConstants.DeleteId);
            _mock.Verify(x => x.Save(It.Is<XElement>(elem => VerifyDelete(elem)),
                                     It.Is<string>(p => VerifyPath(p))));
        }

        [TestMethod]
        public void GetDraftIdReturnsDraftId()
        {
            var draftId = _repository.GetDraftIdForPublishedPost(DraftDataTestConstants.FileId);
            Assert.AreEqual(DraftDataTestConstants.DraftId, draftId);
        }

        [TestMethod]
        public void GetDraftIdReturnsEmptyIfNoDraft()
        {
            var draftId = _repository.GetDraftIdForPublishedPost("SomeId");
            Assert.AreEqual(string.Empty, draftId);
        }

        [TestMethod]
        public void ScheduleAddsScheduledAttribute()
        {
            _repository.Schedule(DraftDataTestConstants.DraftId, DateTime.Today.AddDays(1));
            _mock.Verify(x => x.Save(It.Is<XElement>(elem => VerifySchedule(elem)),
                                    It.Is<string>(p => VerifyPath(p))));
        }

        private bool VerifyCreate(XElement root)
        {
            bool result = false;
            var insert = root.Elements(Elem.Draft)
                .SingleOrDefault(elem => elem.Attribute(Attr.FileId).Value.Equals(DraftDataTestConstants.InsertId));
            result = insert != null;
            if (result)
            {
                result = insert.Attribute(Attr.Title).Value.Equals(DraftDataTestConstants.InsertTitle) &&
                         insert.Attribute(Attr.CatId).Value.Equals(DraftDataTestConstants.InsertCategory);
            }
            return result;
        }

        private bool VerifyUpdate(XElement root)
        {
            bool result = false;
            var update = root.Elements(Elem.Draft)
                .SingleOrDefault(elem => elem.Attribute(Attr.FileId).Value.Equals(DraftDataTestConstants.DraftId));
            result = update != null;
            if (result)
            {
                string newCatIds = string.Format("{0},{1}",
                                                DraftDataTestConstants.UpdateCategory1,
                                                DraftDataTestConstants.UpdateCategory2);
                result = update.Attribute(Attr.Title).Value.Equals(DraftDataTestConstants.UpdateTitle) &&
                         update.Attribute(Attr.CatId).Value.Equals(newCatIds);
            }
            return result;
        }

        private bool VerifyDelete(XElement root)
        {
            return !root.Elements(Elem.Draft)
                       .Any(elem => elem.Attribute(Attr.FileId).Equals(DraftDataTestConstants.DeleteId));
        }

        private bool VerifySchedule(XElement root)
        {
            var element = root.Elements(Elem.Draft)
                              .SingleOrDefault(elem =>
                                  elem.Attribute(Attr.FileId).Value.Equals(DraftDataTestConstants.DraftId));
            bool result = element != null;
            if(result)
            {
                string scheduledDate = DateTime.Today
                                               .AddDays(1)
                                               .ToString(Constants.DateTimeFormat,
                                                         CultureInfo.InvariantCulture);
                result = element.Attribute(Attr.Scheduled).Value.Equals(scheduledDate);
            }
            return result;
        }
    }
}
